import java.io.*;
import java.net.*;

public class SimpleFTPServer {
    private static final int PORT = 2121; // FTP uses port 21 by default, using 2121 to avoid conflicts
    private static final String USERNAME = "user";
    private static final String PASSWORD = "pass";
    private static final String ROOT_DIR = "ftp_root"; // The directory to store uploaded files
    private ServerSocket serverSocket;
    //private String currentDir = ROOT_DIR; // 用于跟踪当前工作目录
    public static void main(String[] args) {
        new File(ROOT_DIR).mkdir(); // Create the root directory if it doesn't exist

        SimpleFTPServer ftpServer = new SimpleFTPServer();
        ftpServer.start();
    }

    public void start() {
        try {
            serverSocket = new ServerSocket(PORT);
            System.out.println("FTP server started on port " + PORT);

            while (true) {
                Socket clientSocket = serverSocket.accept();
                new ClientHandler(clientSocket, ROOT_DIR).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static class ClientHandler extends Thread {
        private Socket clientSocket;
        private BufferedReader reader;
        private BufferedWriter writer;
        private boolean authenticated;
        private ServerSocket dataServerSocket;
        private Socket dataSocket;
        //private String rootDir;
        private long restPosition = 0; // Define restPosition as an instance variable
        private volatile boolean paused = false;
        private String currentDir; // 当前工作目录

        public ClientHandler(Socket clientSocket, String rootDir) {
            this.clientSocket = clientSocket;
            //this.rootDir = rootDir;
            this.currentDir = rootDir; // 初始化当前工作目录为根目录
        }

        @Override
        public void run() {
            try {
                reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                writer = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));

                sendResponse("220 Welcome to Simple FTP Server");

                while (true) {
                    String command = reader.readLine();
                    if (command == null) {
                        break;
                    }

                    System.out.println("Command: " + command);
                    handleCommand(command);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    clientSocket.close();
                    if (dataSocket != null) {
                        dataSocket.close();
                    }
                    if (dataServerSocket != null) {
                        dataServerSocket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        private void handleCommand(String command) throws IOException {
            if (command.startsWith("USER")) {
                handleUser(command);
            } else if (command.startsWith("PASS")) {
                handlePass(command);
            } else if (authenticated && command.startsWith("STOR")) {
                handleStor(command);
            } else if (authenticated && command.startsWith("REST")) {
                handleRest(command);
            } else if (authenticated && command.startsWith("SIZE")) {
                handleSize(command);
            } else if (authenticated && command.startsWith("LIST")) {
                handleList();
            } else if (authenticated && command.startsWith("RETR")) {
                handleRetr(command);
            } else if (authenticated && command.startsWith("PASV")) {
                handlePasv();
            } else if (authenticated && command.startsWith("DELE")) {
                handleDele(command);
            } else if (authenticated && command.startsWith("RMD")) {
                handleRmd(command);
            } else if (authenticated && command.startsWith("CWD")) {
                handleCwd(command);
            } else if (authenticated && command.startsWith("CDUP")) {
                handleCdup();
            } else if (command.startsWith("QUIT")) {
                sendResponse("221 Goodbye");
                clientSocket.close();
            } else if (authenticated && command.startsWith("PAUSE")) {
                handlePause();
            } else if (authenticated && command.startsWith("CONTINUE")) {
                handleContinue();
            } else {
                sendResponse("530 Not logged in");
            }
        }

        private void handleSize(String command) throws IOException {
            if (!authenticated) {
                sendResponse("530 Not logged in");
                return;
            }

            String[] parts = command.split(" ");
            if (parts.length != 2) {
                sendResponse("501 Syntax error in parameters or arguments.");
                return;
            }

            String fileName = parts[1];
            File file = new File(currentDir, fileName);

            if (file.exists()) {
                long fileSize = file.length();
                sendResponse("213 " + fileSize);
            } else {
                sendResponse("550 File not found" + fileName);
            }
        }

        private void handleUser(String command) throws IOException {
            sendResponse("331 Username okay, need password");
        }

        private void handlePass(String command) throws IOException {
            String password = command.split(" ")[1];
            if (USERNAME.equals(USERNAME) && PASSWORD.equals(password)) {
                authenticated = true;
                sendResponse("230 User logged in, proceed");
            } else {
                sendResponse("530 Login incorrect");
            }
        }

        private void handleStor(String command) throws IOException {
            String fileName = command.split(" ")[1];
            sendResponse("150 File status okay; about to open data connection");

            try (Socket dataSocket = dataServerSocket.accept();
                 OutputStream os = new FileOutputStream(new File(currentDir, fileName));
                 InputStream is = dataSocket.getInputStream()) {

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = is.read(buffer)) != -1) {
                    synchronized (this) {
                        while (paused) {
                            wait(); // 等待直到暂停状态被解除
                        }
                    }
                    os.write(buffer, 0, bytesRead);
                }
                sendResponse("226 Transfer complete");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                sendResponse("550 Transfer interrupted");
            } catch (IOException e) {
                sendResponse("550 File unavailable");
            }
        }

        private void handleRest(String command) throws IOException {
            if (!authenticated) {
                sendResponse("530 Not logged in");
                return;
            }
            String[] parts = command.split(" ");
            if (parts.length != 2) {
                sendResponse("501 Syntax error in parameters or arguments.");
                return;
            }
            long offset;
            try {
                offset = Long.parseLong(parts[1]);
            } catch (NumberFormatException e) {
                sendResponse("501 Syntax error in parameters or arguments.");
                return;
            }
            restPosition = offset;
            sendResponse("350 Restarting at " + offset + ". Send STORE or RETRIEVE to initiate transfer.");
        }

        private void handleRetr(String command) throws IOException {
            String[] parts = command.split(" ");
            if (parts.length < 2) {
                sendResponse("500 Syntax error, command unrecognized");
                return;
            }

            String fileName = parts[1];
            File file = new File(currentDir, fileName);

            if (file.exists()) {
                long startPosition = restPosition > 0 ? restPosition : 0;

                sendResponse("150 File status okay; about to open data connection");

                if (dataServerSocket == null) {
                    sendResponse("425 No data connection");
                    return;
                }

                try (Socket dataSocket = dataServerSocket.accept();
                     RandomAccessFile raf = new RandomAccessFile(file, "r");
                     OutputStream os = dataSocket.getOutputStream()) {

                    raf.seek(startPosition);
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = raf.read(buffer)) != -1) {
                        os.write(buffer, 0, bytesRead);
                    }
                    os.flush();
                    sendResponse("226 Transfer complete");
                } catch (IOException e) {
                    sendResponse("550 File unavailable: " + e.getMessage());
                }
            } else {
                sendResponse("550 File not found");
            }
        }

        private void handlePause() throws IOException {
            paused = true;
            sendResponse("200 Transfer paused");
        }

        private void handleContinue() throws IOException {
            synchronized (this) {
                paused = false;
                notify(); // 恢复文件传输
            }
            sendResponse("200 Transfer resumed");
        }

        private void handleDele(String command) throws IOException {
            String[] parts = command.split(" ");
            if (parts.length < 2) {
                sendResponse("500 Syntax error, command unrecognized");
                return;
            }

            String fileName = parts[1];
            File file = new File(currentDir, fileName);

            if (file.exists() && file.isFile()) {
                if (file.delete()) {
                    sendResponse("250 File deleted successfully");
                } else {
                    sendResponse("450 File delete failed");
                }
            } else {
                sendResponse("550 File not found");
            }
        }

        private void handleRmd(String command) throws IOException {
            String[] parts = command.split(" ");
            if (parts.length < 2) {
                sendResponse("500 Syntax error, command unrecognized");
                return;
            }

            String dirName = parts[1];
            File dir = new File(ROOT_DIR, dirName);

            if (dir.exists() && dir.isDirectory()) {
                if (dir.delete()) {
                    sendResponse("250 Directory deleted successfully");
                } else {
                    sendResponse("450 Directory delete failed");
                }
            } else {
                sendResponse("550 Directory not found");
            }
        }

        private void handleCwd(String command) throws IOException {
            if (!authenticated) {
                sendResponse("530 Not logged in");
                return;
            }
        
            String[] parts = command.split(" ");
            if (parts.length != 2) {
                sendResponse("501 Syntax error in parameters or arguments.");
                return;
            }
        
            String newDir = parts[1];
            File dir = new File(/*currentDir,*/ newDir);
            if (dir.isDirectory()) {
                currentDir = dir.getAbsolutePath();
                sendResponse("250 Directory successfully changed.");
            } else {
                sendResponse("550 Directory not found. " + dir);
            }
        }

        private void handleCdup() throws IOException {
            if (!authenticated) {
                sendResponse("530 Not logged in");
                return;
            }
        
            File currentDirFile = new File(currentDir);
            if (currentDirFile.getParentFile() != null) {
                currentDir = currentDirFile.getParentFile().getAbsolutePath();
                sendResponse("250 Directory successfully changed.");
            } else {
                sendResponse("550 Already at root directory.");
            }
        }

        private void handleList() throws IOException {
            File directory = new File(currentDir);
            File[] files = directory.listFiles();
        
            if (files != null) {
                sendResponse("150 Here comes the directory listing");
        
                dataSocket = dataServerSocket.accept();
                try (BufferedWriter dataWriter = new BufferedWriter(new OutputStreamWriter(dataSocket.getOutputStream()))) {
                    for (File file : files) {
                        if (file.isDirectory()) {
                            dataWriter.write(file.getName() + "/\r\n"); // 目录名后添加斜杠
                        } else {
                            dataWriter.write(file.getName() + "\r\n");
                        }
                    }
                    dataWriter.flush();
                } catch (IOException e) {
                    sendResponse("550 Failed to list directory");
                } finally {
                    dataSocket.close();
                }
                sendResponse("226 Directory send OK");
            } else {
                sendResponse("550 Failed to list directory");
            }
        }
        

        private void handlePasv() throws IOException {
            dataServerSocket = new ServerSocket(0);
            int port = dataServerSocket.getLocalPort();

            // 使用地址的本地 IP
            String hostAddress = InetAddress.getLocalHost().getHostAddress();
            String[] addressParts = hostAddress.split("\\.");
            String portPart1 = String.valueOf(port / 256);
            String portPart2 = String.valueOf(port % 256);

            String response = String.format("227 Entering Passive Mode (%s,%s,%s,%s,%s,%s)",
                    addressParts[0], addressParts[1], addressParts[2], addressParts[3], portPart1, portPart2);
            sendResponse(response);
        }

        private void sendResponse(String response) throws IOException {
            System.out.println("Response: " + response);
            writer.write(response + "\r\n");
            writer.flush();
        }
    }
}
